unit GraphicEngine;
interface
uses
  Direct3D9,D3DX9,DXTypes,
  Messages,Windows,SysUtils,MMSystem,
  Config,camera, __include, pathfinding;

//-----------------------------------------------------------------------------
// Graphics types
//-----------------------------------------------------------------------------
type
 PD3DMaterial9Array          = ^TD3DMaterial9Array;
 TD3DMaterial9Array          = array[0..MaxInt div SizeOf(TD3DMaterial9)-1] of TD3DMaterial9;

 PIDirect3DTexture9Array     = ^IDirect3DTexture9Array;
 IDirect3DTexture9Array      = array[0..MaxInt div SizeOf(IDirect3DTexture9)-1] of IDirect3DTexture9;

 PD3DXMaterialArray          = ^TD3DXMaterialArray;
 TD3DXMaterialArray          = array[0..MaxInt div SizeOf(TD3DXMaterial)-1] of TD3DXMaterial;

 TMesh = record
  pMesh:               ID3DXMesh;                // Mesh object in sysmem
  pMeshMaterials:      PD3DMaterial9Array;       // Materials for Mesh
  pMeshTextures:       PIDirect3DTexture9Array;  // Textures for Mesh
  NumMaterials:        Integer;                  // Number of mesh materials
  x,y,z,a,s:           Single;
 end;

 TMeshArray = record
  NumMesh:     integer;
  Mesh:        array [0..42] of Tmesh;
 end;

 pMeshList = ^TMeshList;

 TMeshList = Record
  Mesh:       Tmesh;
  Next:       pMeshList;
 end;

//-----------------------------------------------------------------------------
// Global variables
//-----------------------------------------------------------------------------
var
 g_pD3D:            IDirect3D9              = nil; //Used to create the D3DDevice
 g_pd3dDevice:      IDirect3DDevice9        = nil; //Rendering device

 MyMeshArray:       TMeshArray;
 MyCam:             Tcamera;

  // Pathfinding
 i, j : Integer;
 map : Pmap;
 depart, arrive : t_coord;
 calculate_path : TPathfinding;
 start : single;

//-----------------------------------------------------------------------------
// Functions And Procedures
//-----------------------------------------------------------------------------

function    InitD3D(hWnd: HWND): HRESULT;
function    InitSkybox(size: single): HRESULT;
function    AddMesh(pMeshfile: Pchar; x,y,z,angle,scale: single): HRESULT;

procedure   Cleanup(tab:TMeshArray);
//procedure   SetupMatrices;
procedure   Render(tab:TMeshArray);

function    MsgProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
procedure   HandleKeys(wParam: WPARAM);
Procedure   move;
Procedure   WinMain();

implementation

function    InitD3D(hWnd: HWND): HRESULT;
var
 caps:             D3DCAPS9;
 d3dpp:            TD3DPresentParameters;

begin
 Result:= E_FAIL;

 // Create the D3D object.
 g_pD3D := Direct3DCreate9(D3D_SDK_VERSION);

 if g_pD3D = nil then begin
  MessageBox(0,'Could not point to IDirect3D9','Function InitD3D() Error',MB_ICONERROR);
  Exit;
 end;

 if FAILED(g_pd3d.GetDeviceCaps(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,caps)) then
  MessageBox(0,'Could not GetDeviceCaps','Function InitD3D() Error',MB_ICONEXCLAMATION);

 if (caps.DevCaps = D3DDEVCAPS_HWTRANSFORMANDLIGHT) then
  d3dpp.Flags:= D3DCREATE_HARDWARE_VERTEXPROCESSING;


 FillChar(d3dpp, SizeOf(d3dpp), 0);

 d3dpp.Windowed :=                     Config.windowed;
 d3dpp.FullScreen_RefreshRateInHz :=   D3DPRESENT_RATE_DEFAULT;
 d3dpp.BackBufferWidth :=              Config.WindowsWidth;
 d3dpp.BackBufferHeight :=             Config.WindowsHeight;
 d3dpp.BackBufferFormat :=             D3DFMT_A8R8G8B8;
 d3dpp.BackBufferCount :=              1;
 d3dpp.SwapEffect :=                   D3DSWAPEFFECT_DISCARD;
 d3dpp.hDeviceWindow :=                hWnd;
 d3dpp.EnableAutoDepthStencil :=       True;
 d3dpp.AutoDepthStencilFormat :=       D3DFMT_D16;

 d3dpp.MultiSampleType:=               D3DMULTISAMPLE_8_SAMPLES;
 d3dpp.MultiSampleQuality:=            2;

 Result:= g_pD3D.CreateDevice(D3DADAPTER_DEFAULT,                   // Primary display adapter
                              D3DDEVTYPE_HAL,                       // Device Type HAL or REF
                              hWnd,
                              D3DCREATE_HARDWARE_VERTEXPROCESSING,
                              @d3dpp,                               // @ Direct3DPresentParameters
                              g_pd3dDevice);

 if FAILED(Result) then begin
  Result:= E_FAIL;
  MessageBox(0,'Could not create Direct3D9 Device. You may check the PresentParameters','Function InitD3D() Error',MB_ICONERROR);
  Exit;
 end;

 g_pd3dDevice.SetRenderState(D3DRS_ZENABLE, itrue);     // Turn on the zbuffer
 g_pd3dDevice.SetRenderState(D3DRS_AMBIENT, $ffffff);   // Turn on ambient lighting  $rrvvbb

 Result:= S_OK;
 end;
function    InitSkybox(size: single): HRESULT;
var
  pD3DXMtrlBuffer: ID3DXBuffer;
  d3dxMaterials: PD3DXMaterialArray;
  i: Integer;
  MySkybox: ^Tmesh;

begin
 Result:= E_FAIL;
 MyMeshArray.NumMesh:= 0;

 MyMeshArray.Mesh[0].x := 0 ;
 MyMeshArray.Mesh[0].y := 0 ;
 MyMeshArray.Mesh[0].z := 3*size ;
 MyMeshArray.Mesh[0].a := 0 ;
 MyMeshArray.Mesh[0].s := size ;

 MySkybox := @MyMeshArray.mesh[MyMeshArray.NumMesh];

 if FAILED(D3DXLoadMeshFromX(pSkyBoxfile,               //pFilename:          PChar
                             D3DXMESH_SYSTEMMEM,        //Options:            DWord
                             g_pd3dDevice,              //pD3DDevice:         IDirect3DDevice9
                             nil,                       //ppAdjacency:        PID3DXBuffer
                             @pD3DXMtrlBuffer,          //ppMaterials:        PID3DXBuffer
                             nil,                       //ppEffectInstances:  PID3DXBuffer
                             @MySkyBox.NumMaterials,    //pNumMaterials:      PDWORD
                             MySkyBox.pMesh ))          //out ppMesh:         ID3DXMesh
 then MessageBox(0, 'Could not load Skybox.x', 'Quantum.exe :: File missing ?', MB_OK);

 // We need to extract the material properties and texture names from the pD3DXMtrlBuffer
 d3dxMaterials := pD3DXMtrlBuffer.GetBufferPointer;
 try
  GetMem(Myskybox.pMeshMaterials, SizeOf(TD3DMaterial9)     * Myskybox.NumMaterials);    // Reserve de la memoire pour le pointeur de la taille du tableau :
  GetMem(Myskybox.pMeshTextures,  SizeOf(IDirect3DTexture9) * Myskybox.NumMaterials);    // taille d un fois le nombre ....
 except
  Result:= E_OUTOFMEMORY;
  MessageBox(0,'Out of memory','Quantum.exe',MB_OK);
  Exit;
 end;

 ZeroMemory(Myskybox.pMeshTextures, SizeOf(IDirect3DTexture9)* Myskybox.NumMaterials);    //vide la plage

 for i:= 0 to Myskybox.NumMaterials-1 do begin //-2 no roof
  Myskybox.pMeshMaterials[i] := d3dxMaterials[i].MatD3D;                      // Copy the material
  Myskybox.pMeshMaterials[i].Ambient := Myskybox.pMeshMaterials[i].Diffuse;   // Set the ambient color for the material
  case i of
   0 : d3dxMaterials[i].pTextureFilename := 'Data\Textures\Skybox\pos_y.bmp';
   1 : d3dxMaterials[i].pTextureFilename := 'Data\Textures\Skybox\pos_x.bmp';
   2 : d3dxMaterials[i].pTextureFilename := 'Data\Textures\Skybox\neg_x.bmp';
   3 : d3dxMaterials[i].pTextureFilename := 'Data\Textures\Skybox\neg_z.bmp';
   4 : d3dxMaterials[i].pTextureFilename := 'Data\Textures\Skybox\pos_z.bmp';
   5 : d3dxMaterials[i].pTextureFilename := 'Data\Textures\Skybox\neg_y.bmp';
  end;

  if (d3dxMaterials[i].pTextureFilename <> nil) and (StrLen(d3dxMaterials[i].pTextureFilename) > 0) then
   if FAILED(D3DXCreateTextureFromFile(g_pd3dDevice,                       //Device:         IDirect3DDevice9;
                                       d3dxMaterials[i].pTextureFilename,  //PSrcFile:       pChar
                                       Myskybox.pMeshTextures[i]))         //out ppTexture:  IDirect3DTexture9
    then MessageBox(0,'Could not find Skybox texture','Quantum.exe', MB_OK);
 end;

 pD3DXMtrlBuffer:= nil;
 Result:= S_OK;
 MyCam:=Tcamera.create;
end;

function    AddMesh(pMeshfile: Pchar; x,y,z,angle,scale: single): HRESULT;
var
  pD3DXMtrlBuffer: ID3DXBuffer;
  d3dxMaterials: PD3DXMaterialArray;
  MyMesh:     ^Tmesh;

  i:          Integer;
  strtemp:    string;
  pchartemp:  pchar;
begin
  Result:= E_FAIL;

  if MyMeshArray.NumMesh > 41 then begin
   MessageBox(0,'Mesh Array is full !','Quantum.exe : LoadMyMesh Error !', MB_OK);
   Exit;
  end;
  inc(MyMeshArray.NumMesh);

  MyMeshArray.Mesh[MyMeshArray.NumMesh].x := x;
  MyMeshArray.Mesh[MyMeshArray.NumMesh].y := y;
  MyMeshArray.Mesh[MyMeshArray.NumMesh].z := z;
  MyMeshArray.Mesh[MyMeshArray.NumMesh].a := angle;
  MyMeshArray.Mesh[MyMeshArray.NumMesh].s := scale;

  MyMesh := @MyMeshArray.mesh[MyMeshArray.NumMesh];

  // Load the mesh from the file
  if FAILED(D3DXLoadMeshFromX(pMeshfile,             //pFilename:          PChar
                              D3DXMESH_SYSTEMMEM,    //Options:            DWord
                              g_pd3dDevice,          //pD3DDevice:         IDirect3DDevice9
                              nil,                   //ppAdjacency:        PID3DXBuffer
                              @pD3DXMtrlBuffer,      //ppMaterials:        PID3DXBuffer
                              nil,                   //ppEffectInstances:  PID3DXBuffer
                              @MyMesh.NumMaterials,  //pNumMaterials:      PDWORD
                              MyMesh.pMesh           //out ppMesh:         ID3DXMesh
                             )) then
    MessageBox(0, 'Could not find the mesh', 'Quantum.exe', MB_OK);

  // extract the material properties and texture names from the pD3DXMtrlBuffer
  d3dxMaterials := pD3DXMtrlBuffer.GetBufferPointer;
  try
    GetMem(MyMesh.pMeshMaterials, SizeOf(TD3DMaterial9)*MyMesh.NumMaterials);
    GetMem(MyMesh.pMeshTextures, SizeOf(IDirect3DTexture9)*MyMesh.NumMaterials);
  except
    Result:= E_OUTOFMEMORY;
    MessageBox(0, 'OUT OF MEMORY READ ERROR', 'Quantum.exe', MB_OK);
    Exit;
  end;
  ZeroMemory(MyMesh.pMeshTextures, SizeOf(IDirect3DTexture9)*MyMesh.NumMaterials);
  for i:= 0 to MyMesh.NumMaterials-1 do
   begin
    MyMesh.pMeshMaterials[i] := d3dxMaterials[i].MatD3D;                 // Copy the material
    MyMesh.pMeshMaterials[i].Ambient:=MyMesh.pMeshMaterials[i].Diffuse;  // Set the ambient color for the material (D3DX does not do this)
    MyMesh.pMeshTextures[i] := nil;
    if (d3dxMaterials[i].pTextureFilename <> nil) and (StrLen(d3dxMaterials[i].pTextureFilename) > 0) then begin

//     strtemp:=StrPas(pmeshdir) + StrPas(d3dxMaterials[i].pTextureFilename)+ Chr(0);
//     strtemp[Length(strtemp)]:= #0;
//     d3dxMaterials[i].pTextureFilename:= @strtemp;
//     MessageBox(0,d3dxMaterials[i].pTextureFilename,'test', MB_OK);

     if FAILED(D3DXCreateTextureFromFile(g_pd3dDevice,d3dxMaterials[i].pTextureFilename,MyMesh.pMeshTextures[i])) then
      MessageBox(0,'Could not find texture file','Quantum.exe : Load Mesh Error', MB_OK);
    end;
   end;
  pD3DXMtrlBuffer:= nil;
  Result:= S_OK;
end;

procedure   Cleanup(tab:TMeshArray);
var
 i1,i2: Integer;
begin
 for i1 := 0 to tab.NumMesh do begin
   if (tab.mesh[i1].pMeshMaterials <> nil) then
     FreeMem(tab.mesh[i1].pMeshMaterials);
   if (tab.mesh[i1].pMeshTextures <> nil) then begin
     i2:= 0;
     while (i2 < tab.mesh[i1].NumMaterials) do begin
       tab.mesh[i1].pMeshTextures[i2]:= nil;
       Inc(i2);
     end;
     FreeMem(tab.mesh[i1].pMeshTextures);
   end;
   tab.mesh[i1].pMesh:= nil;
 end;

 g_pd3dDevice:= nil;
 g_pD3D:= nil;

end;

procedure move();
begin
  start := GetTickCount;
end;

procedure   Render(tab:TMeshArray);
var
  i1,i2: Integer;
  matWorld, matProj, temp: TD3DMatrix;
begin
  // Clear the backbuffer and the zbuffer
  g_pd3dDevice.Clear(0, nil, D3DCLEAR_TARGET or D3DCLEAR_ZBUFFER,
                     D3DCOLOR_XRGB(0,0,25), 1.0, 0);

  // Begin the scene
  if SUCCEEDED(g_pd3dDevice.BeginScene) then begin

   //Init Matrices;
    D3DXMatrixTranslation(&matWorld,0,0,0);
    D3DXMatrixScaling(&matWorld,1,1,1);
    g_pd3dDevice.SetTransform(D3DTS_WORLD, matWorld);
    g_pd3dDevice.SetTransform(D3DTS_VIEW,mycam.getMatView);
    D3DXMatrixPerspectiveFovLH(matProj, D3DX_PI/4, Config.ScreenRatio, 1.0, Config.DistAffMax);
    g_pd3dDevice.SetTransform(D3DTS_PROJECTION, matProj);

    // Meshes are divided into subsets, one for each material.
    // Render them in a loop
    for i1 := 0 to tab.NumMesh do begin

      temp:= matWorld;
      D3DXMatrixTranslation(&matWorld,tab.mesh[i1].x,tab.mesh[i1].z,tab.mesh[i1].y);

      if tab.mesh[i1].a <> 0 then
       D3DXMatrixRotationY(&matWorld,tab.mesh[i1].y);

      if tab.Mesh[i1].s <> 1 then
       D3DXMatrixScaling(&matWorld,tab.Mesh[i1].s,tab.Mesh[i1].s, tab.Mesh[i1].s);

      g_pd3dDevice.SetTransform(D3DTS_WORLD, matWorld);

      matWorld:= temp;

     for i2:= 0 to tab.mesh[i1].NumMaterials-1 do begin
       // Set the material and texture for this subset
       g_pd3dDevice.SetMaterial(tab.mesh[i1].pMeshMaterials[i2]);
       g_pd3dDevice.SetTexture(0,tab.mesh[i1].pMeshTextures[i2]);
       // Draw the mesh subset
       tab.Mesh[i1].pMesh.DrawSubset(i2);
    end; end;
    // End the scene
    g_pd3dDevice.EndScene;
  end;

  // Present the backbuffer contents to the display
  g_pd3dDevice.Present(nil, nil, 0, nil);
end;

//procedure   GetMatPos(mOut);
//begin
//
//end;

function    MsgProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
 case uMsg of
  
  WM_DESTROY:
             begin
               Cleanup(MyMeshArray);
               PostQuitMessage(0);
               Result:= 0;
               Exit;
             end;

  WM_KEYDOWN : HandleKeys(wParam);

  WM_MOUSEMOVE: begin
                 //GET_X_LPARAM(lParam);
                 //GET_Y_LPARAM(lParam);
                end;

  WM_CREATE : begin
                    New(map);
                    SetLength(map.environnement, 201, 201);
                    for i := 1 to 200 do
                      begin
                        for j := 1 to 200 do
                          begin
                             map.environnement[i, j] := true;
                          end;
                      end;

                      for i := 50 to 100 do
                      begin
                        for j := 1 to 100 do
                          begin
                             map.environnement[i, j] := false;
                          end;
                      end;

                    map.longueur := 200;
                    map.largeur := 200;

                    depart.x := 1;
                    depart.y := 1;

                    arrive.x := 15;
                    arrive.y := 15;

                    calculate_path := TPathfinding.Create(depart, arrive, map);
                    calculate_path.main();
              end;

 end;

 Result:= DefWindowProc(hWnd, uMsg, wParam, lParam);
end;
procedure   HandleKeys(wParam: WPARAM);
begin
 case wParam of
  VK_UP :       MyCam.Up;
  VK_DOWN :     MyCam.Down;
  VK_LEFT :     MyCam.Left;
  VK_RIGHT :    MyCam.Right;

  VK_NUMPAD1 :  MyCam.ZoomIn ;
  VK_NUMPAD0 :  MyCam.ZoomOut ;
  VK_F1 : move;
  VK_F2 : mycam.RotateTrigo;
  VK_F3 : mycam.RotateAntiTrigo;

//  VK_SPACE :    AddMesh(pmesh1,5,5,5,0,1);

  VK_ESCAPE :   PostQuitMessage(WM_QUIT);
end;
end;

procedure updateGame(time : single);
var
i : integer;
begin
 i := Trunc(time / 500);
// MyMeshArray.Mesh[1].x:= 5*Cos(GetTickCount/1000);
// MyMeshArray.Mesh[1].y:= 5*Sin(GetTickCount/1000);

  MyMeshArray.Mesh[2].x:= calculate_path.chemin[i].x;
  MyMeshArray.Mesh[2].y:= calculate_path.chemin[i].y;

end;

procedure   WinMain();
var
  hWindow: HWND;
  msg: TMsg;
  wc: TWndClassEx;

  i: single;

begin
  // TWndClassEx =
  wc.cbSize:=         SizeOf(TWndClassEx);
  wc.style:=          CS_CLASSDC;
  wc.lpfnWndProc:=    @MsgProc;
  wc.cbClsExtra:=     0;
  wc.cbWndExtra:=     0;
  wc.hInstance:=      0;
  wc.hIcon:=          0;
  wc.hCursor:=        0;
  wc.hbrBackground:=  0;
  wc.lpszMenuName:=   nil;
  wc.lpszClassName:=  'D3D Quantum';
  wc.hIconSm:=        0;

  // Register the window class
  wc.hInstance:= GetModuleHandle(nil);
  wc.hIcon:= LoadCursorFromFile(QIcon);
  wc.hCursor:= LoadCursorFromFile(QCursor);
  RegisterClassEx(wc);

  // Create the application's window
  hWindow := CreateWindow('D3D Quantum',           // lpClassName:      PChar
                          Config.WindowsName,      // lpWindowName:     PChar
                          WS_OVERLAPPEDWINDOW,     // dwStyle:          DWORD
                          0,                       // X:                Integer
                          0,                       // Y:                Integer
                          Config.WindowsWidth,     // nWidth:           Integer
                          Config.Windowsheight,    // nHeight:          Integer
                          0,                       // hWndParent:       HWND
                          0,                       // hMenu:            HMENU
                          wc.hInstance,            // hInstance:        HINST
                          nil );                   // lpParam:          Pointer


  // Initialize Direct3D
  if SUCCEEDED(InitD3D(hWindow)) then
  if SUCCEEDED(InitSkybox(mapsize))       then
   begin
     AddMesh(pmesh1,10,10,-22,0,1);
     AddMesh(pmesh1,-10,-10,-22,0,1);

     ShowWindow(hWindow, SW_SHOWDEFAULT);
     UpdateWindow(hWindow);

     

     // Enter the message loop
     FillChar(msg, SizeOf(msg), 0);
     while (msg.message <> WM_QUIT) do begin

      updateGame(getTickCount - start);
      if PeekMessage(msg, 0, 0, 0, PM_REMOVE) then begin
        TranslateMessage(msg);
        DispatchMessage(msg);
      end else
      Render(MyMeshArray);

     end;

   end;

  UnregisterClass('D3D Quantum', wc.hInstance);
end;

end.
